package com.lemon.components.security;

import java.nio.ByteBuffer;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.junit.Assert;

public class SecurityTool {

	/**
	 * 
	 * <font size="3" color="red">生成密钥对</font><br>
	 * <ol>
	 * <li>公钥获取：keyPair.getPublic()</li>
	 * <li>私钥获取：keyPair.getPrivate()</li>
	 * </ol>
	 * @param algorithmEnum 算法名称
	 * @param keySize 密钥长度
	 * @return <font size="2" color="blue">密钥对</font>
	 */
	public static KeyPair generateKey(AlgorithmEnum algorithmEnum,int keySize){
		try {
			KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithmEnum.getCode());
			keyPairGenerator.initialize(keySize);
			KeyPair keyPair = keyPairGenerator.generateKeyPair();
			return keyPair;
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * 
	 * <font size="3" color="red">从Base64文件中获取私钥</font><br>
	 * <ol>
	 * <li>内含获取公钥方法</li>
	 * </ol>
	 * @param pkey base64编码的私钥字符串
	 * @return
	 * @throws Exception <font size="2" color="blue">异常</font>
	 */
	public static Key getPrivateKey(String keyBase64,AlgorithmEnum algorithmEnum) throws Exception{
		byte[] keyBytes = Base64.decodeBase64(keyBase64);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(algorithmEnum.getCode());
		return keyFactory.generatePrivate(pkcs8KeySpec); 
	}
	
	/**
	 * 
	 * <font size="3" color="red"></font><br>
	 * <ol>
	 * <li></li>
	 * </ol>
	 * @param keyBase64
	 * @param algorithmEnum
	 * @return
	 * @throws Exception <font size="2" color="blue"></font>
	 */
	public static Key getPublicKey(String keyBase64,AlgorithmEnum algorithmEnum) throws Exception{
		byte[] keyBytes = Base64.decodeBase64(keyBase64);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(algorithmEnum.getCode());
		return keyFactory.generatePublic(pkcs8KeySpec); 
	}
	
	/**
	 * 
	 * <font size="3" color="red"></font><br>
	 * <ol>
	 * <li></li>
	 * </ol>
	 * @param src
	 * @param algorithmEnum
	 * @return
	 * @throws Exception <font size="2" color="blue"></font>
	 */
	public static String digest(String src,AlgorithmEnum algorithmEnum) throws Exception{
		MessageDigest md = MessageDigest.getInstance(algorithmEnum.getCode());
		md.update(src.getBytes());
		return Hex.encodeHexString(md.digest());
	}
	
	public static String signWithPrivateKey(String src,AlgorithmEnum algorithmEnum,PrivateKey privateKey) throws Exception{
		StringBuilder signStr = new StringBuilder();
		Signature signature = Signature.getInstance(algorithmEnum.getCode());
		byte[] work = src.getBytes();
		signature.initSign(privateKey);
		for(int i=0,j=work.length/50; i <= j; i++){
			signature.update(work, i*50, i==j?work.length%50:50);
			signStr.append(Hex.encodeHexString(signature.sign()));
		}
		return signStr.toString();
	}
	
	public static boolean verifyWithPublicKey(String src,String sign,AlgorithmEnum algorithmEnum,PublicKey publicKey) throws Exception{
		Signature signature = Signature.getInstance(algorithmEnum.getCode());
		signature.initVerify(publicKey);
		byte[] work = src.getBytes();
		boolean result = true;
		for(int i=0,j=work.length/50; i < j; i++){
			signature.update(work, i*50, i==j?work.length%50:50);
			boolean r = signature.verify(Arrays.copyOfRange(Hex.decodeHex(sign.toString().toCharArray()), i*64, ((i+1)*64)));
			Assert.assertTrue(r);
			if(!r){
				result = r;
			}
		}
		return result;
	}
	
	/**
	 * 
	 * <font size="3" color="red"></font><br>
	 * <ol>
	 * <li></li>
	 * </ol>
	 * @param src 加密原数据
	 * @param key 加密密钥
	 * @return 加密字符
	 * @throws Exception <font size="2" color="blue"></font>
	 */
	public static String encryptWithKey(String src,Key key) throws Exception{
		StringBuilder encryptStr = new StringBuilder();
		Cipher cipher = Cipher.getInstance(key.getAlgorithm());
		cipher.init(Cipher.ENCRYPT_MODE, key);
		byte[] work = src.getBytes();
		for(int i=0,j=work.length/50; i <= j; i++){
			
			byte[] tmp = cipher.doFinal(work, i*50, i==j?work.length%50:50);
			encryptStr.append(Hex.encodeHexString(tmp));
		}
		return encryptStr.toString();
	}
	
	/**
	 * 
	 * <font size="3" color="red"></font><br>
	 * <ol>
	 * <li></li>
	 * </ol>
	 * @param encryptStr 加密字符
	 * @param key 解密密钥
	 * @return 解密字符
	 * @throws Exception <font size="2" color="blue"></font>
	 */
	public static String decryptWithKey(String encryptStr,Key key) throws Exception{
		byte[] src = Hex.decodeHex(encryptStr.toString().toCharArray());
		Cipher cipher = Cipher.getInstance(key.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, key);
		ByteBuffer byteBuffer = ByteBuffer.allocate(2048);
		for(int i=0,j=src.length/64; i < j; i++){
			byte[] tmp = cipher.doFinal(src,i*64,i==j?src.length%64:64);
			byteBuffer.put(tmp);
		}
		String decryptStr = new String(Arrays.copyOf(byteBuffer.array(), byteBuffer.position()));
		return decryptStr;
	}
}
